/**
 * 项目名：化工企业安全生产信息化管理平台
 * 日期：    2020-04-18 23:54:05
 * Copyright (c) 2015- -版权所有
 */

package com.joysuch.wwyt.core.service.impl;


import cn.hutool.core.collection.CollectionUtil;
import cn.zlg.common.util.CollectionUtils;
import com.google.common.collect.Lists;
import com.joysuch.wwyt.common.constant.Constant;
import com.joysuch.wwyt.core.bean.*;
import com.joysuch.wwyt.core.bean.BaseMenuFunctionListBean.SearchBaseMenuFunction;
import com.joysuch.wwyt.core.common.ajax.ResultBean;
import com.joysuch.wwyt.core.common.util.PageDataUtils;
import com.joysuch.wwyt.core.common.util.PageDataUtils.PageDataConvert;
import com.joysuch.wwyt.core.entity.BaseMenuFunction;
import com.joysuch.wwyt.core.entity.dsl.QBaseMenuFunction;
import com.joysuch.wwyt.core.enums.MenuTypes;
import com.joysuch.wwyt.core.enums.Status;
import com.joysuch.wwyt.core.repository.BaseMenuFunctionDao;
import com.joysuch.wwyt.core.security.ShiroUser;
import com.joysuch.wwyt.core.service.AuthorityService;
import com.joysuch.wwyt.core.service.BaseMenuFunctionService;
import com.joysuch.wwyt.core.support.Context;
import com.joysuch.wwyt.licence.bean.LicenceCacheDto;
import com.joysuch.wwyt.licence.service.LicenceService;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Predicate;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Assert;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.*;
import java.util.stream.Collectors;

import static com.joysuch.wwyt.common.constant.Constant.DEFAULT_MENU_ICON;

@Service
@Transactional
@Slf4j
public class BaseMenuFunctionServiceImpl implements BaseMenuFunctionService {

    @Autowired
    private BaseMenuFunctionDao dao;
    @Autowired
    private AuthorityService authorityService;
    @Value("${wwyt.base.serverUrl}")
    private String serverUrl;
    @Autowired
    private LicenceService licenceService;

    @Override
    public BaseMenuFunction add(BaseMenuFunctionEditBean entity) {
        // 验证类型及父菜单
        validateTypeAndParent(entity);
        //parentId+name
        int count = 0;
        count = getCount(entity);
        if (count > 0) {
            throw new IllegalArgumentException("此名称已存在");
        }
        BaseMenuFunction content = new BaseMenuFunction();
        BeanUtils.copyProperties(entity, content, "id");
        if (entity.getParentId() != null) {
            content.setParent(dao.getOne(entity.getParentId()));
            content.setDepth(content.getParent().getDepth() + 1);
        } else {
            content.setDepth(1);
        }
        //菜单隐藏状态和app菜单显示状态默认打开
        content.setShowState(1);
        content = dao.save(content);
        return content;
    }

    private int getCount(BaseMenuFunctionEditBean entity) {
        int count;
        if (entity.getParentId() != null) {
            count = dao.countByNameAndParentId(entity.getName(), entity.getParentId());
        } else {
            List<BaseMenuFunction> baseMenuFunctions = dao.selectByName(entity.getName());
            count = (int) baseMenuFunctions.stream().filter(r -> r.getParent() == null).count();
        }
        return count;
    }

    private int getAppCount(String appName, Long parentId) {
        int nu = 0;
        if (parentId != null) {
            nu = dao.countByAppNameAndParentId(appName, parentId);
        } else {
            nu = dao.countByAppNameAndParentIdIsNull(appName);
        }
        return nu;
    }

    private void validateTypeAndParent(BaseMenuFunctionEditBean entity) {
        if (entity.getParentId() == null) {
            Assert.isTrue(MenuTypes.FOLDER.is(entity.getType()) || MenuTypes.MENU.is(entity.getType()), "按钮必须指定父节点");
        } else {
            BaseMenuFunction parent = dao.getOne(entity.getParentId());
            Assert.isTrue(!MenuTypes.BUTTON.is(parent.getType()), "不能指定按钮为父菜单");
            if (MenuTypes.BUTTON.is(entity.getType())) {
                Assert.isTrue(MenuTypes.MENU.is(parent.getType()), "按钮的父菜单类型必须为菜单");
            }
            if (MenuTypes.MENU.is(entity.getType())) {
                Assert.isTrue(MenuTypes.FOLDER.is(parent.getType()), "菜单的父菜单类型必须为菜单集合");
            }
            if (MenuTypes.FOLDER.is(entity.getType())) {
                Assert.isTrue(MenuTypes.FOLDER.is(parent.getType()), "菜单集合的父菜单类型必须为菜单集合");
                Assert.isTrue(parent.getDepth() == 1, "所选父菜单层级不为1，无法新增菜单集合，请重新设置！");
            }
            if (entity.getId() != null) {
                Assert.isTrue(!entity.getId().equals(entity.getParentId()), Status.NOT_SUPPORT_SELF_PARENT.getMsg());
            }
        }
    }

    @Override
    public BaseMenuFunction update(BaseMenuFunctionEditBean entity) {
        validateTypeAndParent(entity);
        BaseMenuFunction content = dao.findById(entity.getId()).get();
        BaseMenuFunction parent = content.getParent();
        Long parentId = entity.getParentId();
        int count = 0;
        boolean b = !content.getName().equals(entity.getName()) || (parent == null && parentId != null)
                || (parent != null && parentId == null) || (parent != null && parentId != null && !parentId.equals(parent.getId()));
        if (b) {
            count = getCount(entity);
        }
        if (count > 0) {
            throw new IllegalArgumentException("此名称已存在");
        }
        BeanUtils.copyProperties(entity, content, "id","modelId");
        if (entity.getParentId() != null) {
            content.setParent(dao.getOne(entity.getParentId()));
            content.setDepth(content.getParent().getDepth() + 1);
        } else {
            content.setParent(null);
            content.setDepth(1);
        }
        content = dao.save(content);
        return content;
    }

    @Override
    public Page<BaseMenuFunctionListBean> findByPage(Pageable page, SearchBaseMenuFunction condition) {
        Predicate predicate = prepareListSearchCondition(condition);
        LicenceCacheDto licenceCacheDto = licenceService.getLicenceCacheDto(Context.getCurrentTenantId(), Context.getCurrentOrgCode());
        Set<String> blackUrlSet = licenceCacheDto.getDecryptedBlackUrlSet();

        QBaseMenuFunction q = QBaseMenuFunction.baseMenuFunction;
        Predicate p = ExpressionUtils.and(null, q.url.in(blackUrlSet));

        Set<Long> blackParentIdSet = new HashSet<>();
        Iterable<BaseMenuFunction> blackMenuIterable = dao.findAll(p);
        blackMenuIterable.forEach(baseMenuFunction -> {
            blackParentIdSet.add(baseMenuFunction.getId());
        });
        if (CollectionUtil.isNotEmpty(blackUrlSet)) {
            predicate = ExpressionUtils.and(predicate, q.url.notIn(blackUrlSet).or(q.url.isNull()));
        }
        if (CollectionUtil.isNotEmpty(blackParentIdSet)) {
            predicate = ExpressionUtils.and(predicate, q.parent.id.notIn(blackParentIdSet).or(q.parent.id.isNull()));
        }

        Page<BaseMenuFunction> data = predicate == null ? dao.findAll(page) : dao.findAll(predicate, page);

        return PageDataUtils.convertPageData(data, new PageDataConvert<BaseMenuFunctionListBean, BaseMenuFunction>() {
            @Override
            public BaseMenuFunctionListBean convert(BaseMenuFunction f) {
                BaseMenuFunctionListBean bean = new BaseMenuFunctionListBean();
                BeanUtils.copyProperties(f, bean);
                try {
                    if (f.getParent() != null) {
                        bean.setParentName(f.getParent().getName());
                    }
                } catch (Exception e) {
                    log.warn("系统菜单查询异常： id={} 找不到上级菜单", f.getId());
                }
                bean.setTypeName(MenuTypes.findNameByCode(f.getType()));
                return bean;
            }

        });
    }

    private Predicate prepareListSearchCondition(SearchBaseMenuFunction condition) {
        Predicate p = null;
        QBaseMenuFunction q = QBaseMenuFunction.baseMenuFunction;
        if (condition.getName() != null) {
            p = ExpressionUtils.and(p, q.name.contains(condition.getName()));
        }
        if (condition.getUrl() != null) {
            p = ExpressionUtils.and(p, q.url.eq(condition.getUrl()));
        }
        if (condition.getPerm() != null) {
            p = ExpressionUtils.and(p, q.perm.eq(condition.getPerm()));
        }
        if (condition.getType() != null) {
            p = ExpressionUtils.and(p, q.type.eq(condition.getType()));
        }
        if (condition.getParentId() != null) {
            p = ExpressionUtils.and(p, q.parent.id.eq(condition.getParentId()));
        }
        if (condition.getShowOrder() != null) {
            p = ExpressionUtils.and(p, q.showOrder.eq(condition.getShowOrder()));
        }
        if (condition.getIcon() != null) {
            p = ExpressionUtils.and(p, q.icon.eq(condition.getIcon()));
        }
        if (condition.getDepth() != null) {
            p = ExpressionUtils.and(p, q.depth.eq(condition.getDepth()));
        }
        if (condition.getAppFlag() != null) {
            p = ExpressionUtils.and(p, q.appFlag.eq(condition.getAppFlag()));
        }
        return p;
    }

    @Override
    public void delete(Long id) {
        // 校验业务逻辑是否允许删除。如本数据被其他业务关联，删除后导致其他业务不可用。不能删除时将抛出业务异常
        checkBussinessRestrictions(id);
        //删除待办默认生成的“全部按钮”
        BaseMenuFunction baseMenuFunction = dao.findFirstByParentIdAndType(id, MenuTypes.BUTTON.getCode());
        if (baseMenuFunction != null) {
            dao.deleteById(baseMenuFunction.getId());
        }
        dao.deleteById(id);
    }

    /**
     * 校验业务逻辑是否允许删除。如本数据被其他业务关联，删除后导致其他业务不可用
     *
     * @param id 要删除的业务ID
     */
    private void checkBussinessRestrictions(Long id) {
    }

    @Override
    public BaseMenuFunction findById(Long id) {
        return dao.findById(id).orElse(null);
    }

    @Override
    public BaseMenuFunctionDetailBean getById(Long id) {
        BaseMenuFunction entity = findById(id);
        if (entity == null) {
            return null;
        }
        BaseMenuFunctionDetailBean bean = new BaseMenuFunctionDetailBean();
        BeanUtils.copyProperties(entity, bean);
        if (entity.getParent() != null) {
            bean.setParentId(entity.getParent().getId());
            bean.setParentName(entity.getParent().getName());
        }
        bean.setTypeName(MenuTypes.findNameByCode(entity.getType()));
        if (entity.getFastScreenImgId() != null) {
            BaseAttachmentBean fastScreenImg = new BaseAttachmentBean();
            fastScreenImg.setId(entity.getFastScreenImgId());
            fastScreenImg.setDownloadUrl(serverUrl + Constant.IMG_DOWNLOAD_URL_PREFIX + entity.getFastScreenImgId());
            bean.setFastScreenView(fastScreenImg);
        }
        return bean;
    }

    @Override
    public List<SimpleSelectListBean> parentList(String type, Long parentId, Long tenantId, String orgCode) {
        QBaseMenuFunction q = QBaseMenuFunction.baseMenuFunction;
        Predicate p = null;
        if (type == null) {
            p = ExpressionUtils.or(q.type.eq(MenuTypes.FOLDER.getCode()), q.type.eq(MenuTypes.MENU.getCode()));
        } else {
            p = q.type.eq(type);
        }
        if (parentId != null) {
            p = ExpressionUtils.and(p, q.id.eq(parentId));
        }
        p = ExpressionUtils.and(p, q.showState.ne(0));
        Iterable<BaseMenuFunction> all = dao.findAll(p);
        LicenceCacheDto licenceCacheDto = licenceService.getLicenceCacheDto(tenantId, orgCode);
        Set<String> blackUrlSet = licenceCacheDto.getDecryptedBlackUrlSet();
        List<SimpleSelectListBean> list = new ArrayList<>();
        for (BaseMenuFunction m : all) {
            if (blackUrlSet.contains(m.getUrl())) {
                continue;
            }
            SimpleSelectListBean bean = new SimpleSelectListBean();
            bean.setLabel(m.getName() + "(" + MenuTypes.findNameByCode(m.getType()) + ")");
            bean.setValue(m.getId());
            list.add(bean);
        }
        return list;
    }

    @Override
    public ResultBean getTopFun() {
        Subject subject = SecurityUtils.getSubject();
        ShiroUser user = (ShiroUser) subject.getPrincipal();
        List<BaseMenuFunction> topMenu = null;
        // 顶级菜单无子菜单的不显示
        List<AuthorityBean> functions = authorityService.queryAuthorityByUserId(user.getRealUserId(),
                user.isSuperAdmin());
        List<Long> functionIds = functions.stream().map(AuthorityBean::getId).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(functionIds)) {
            topMenu = dao.findByIds(functionIds);
        }
        ArrayList<BaseMenuFunctionFastScreenBean> result = Lists.newArrayList();
        if (CollectionUtils.isEmpty(topMenu)) {
            return ResultBean.wrapSuccessfulResult(result);
        }

        topMenu.forEach(each -> {
            BaseMenuFunctionFastScreenBean fastScreenBean = new BaseMenuFunctionFastScreenBean();
            BeanUtils.copyProperties(each, fastScreenBean);
            if (each.getFastScreenImgId() != null) {
                BaseAttachmentBean viewImg = new BaseAttachmentBean();
                viewImg.setId(each.getFastScreenImgId());
                viewImg.setDownloadUrl(serverUrl + Constant.IMG_DOWNLOAD_URL_PREFIX + each.getFastScreenImgId());
                fastScreenBean.setFastScreenView(viewImg);
            }
            result.add(fastScreenBean);
        });
        return ResultBean.wrapSuccessfulResult(result);
    }

    /**
     * 设置菜单隐藏状态
     *
     * @param bean
     */
    @Override
    public BaseMenuFunction saveState(BaseMenuFunctionStateBean bean) {
        BaseMenuFunction function = dao.findById(bean.getId()).orElseThrow(() -> new IllegalArgumentException("菜单不存在"));
        function.setShowState(bean.getShowState());
        BaseMenuFunction save = dao.save(function);
        return save;
    }

    @Override
    public Long updateExMenu(BaseMenuFunctionEditBean entity) {
        validateTypeAndParent(entity);
        Optional<BaseMenuFunction> optionalContent = dao.findById(entity.getId());
        if (optionalContent.isPresent()) {
            BaseMenuFunction content = optionalContent.get();
            Long parentId = entity.getParentId();
            if (parentId == null) {
                throw new IllegalArgumentException("父菜单不存在，请检查");
            }
            int count = 0;
            if (!content.getName().equals(entity.getName())) {
                count = getCount(entity);
            }
            if (count > 0) {
                throw new IllegalArgumentException("菜单名称已存在");
            }
            BeanUtils.copyProperties(entity, content, "id");
            content.setParent(content.getParent());
            content.setDepth(content.getParent().getDepth() + 1);
            content.setModelId(content.getModelId());
            content.setTenentId(content.getTenentId());
            content.setOrgCode(content.getOrgCode());
            if(content.getDepth() == 2){
                content.setIcon(DEFAULT_MENU_ICON);
            }
            content = dao.save(content);
            dao.updateParentId(parentId, entity.getId());
            return content.getId();
        } else {
            //添加菜单
            return addExMenu(entity);
        }
    }

    @Override
    public Long addExMenu(BaseMenuFunctionEditBean entity) {
        // 验证类型及父菜单
        validateTypeAndParent(entity);
        int count = getCount(entity);
        if (count > 0) {
            throw new IllegalArgumentException("菜单名称已存在");
        }
        if (entity.getParentId() == null) {
            throw new IllegalArgumentException("父菜单不存在，请检查");
        }
        BaseMenuFunction content = new BaseMenuFunction();
        BeanUtils.copyProperties(entity, content, "id");
        BaseMenuFunction parentMenu = dao.getOne(entity.getParentId());
        content.setParent(parentMenu);
        content.setTenentId(parentMenu.getTenentId());
        content.setOrgCode(parentMenu.getOrgCode());
        content.setDepth(content.getParent().getDepth() + 1);
        if(content.getDepth() == 2){
            content.setIcon(DEFAULT_MENU_ICON);
        }
        //菜单隐藏状态和app菜单显示状态默认打开
        content.setShowState(1);
        content = dao.save(content);
        return content.getId();
    }

    @Override
    public void deleteByExMenuId(Long id) {
        if(null != id){
            dao.deleteByExMenuId(id);
        }
    }

}
